package cs235.sort;

public class QuickSort extends TannersSort implements Sort{
    /**
     * Quicksort algorithm.
     * @param a an array of Comparable items.
     */
    public static void quicksort( Comparable [ ] a )
    {
        quicksort( a, 0, a.length - 1 );
    }

    private static final int CUTOFF = 10;

    /**
     * Method to swap to elements in an array.
     * @param a an array of objects.
     * @param index1 the index of the first object.
     * @param index2 the index of the second object.
     */
    public static final void swapReferences( Object [ ] a, int index1, int index2 )
    {
        Object tmp = a[ index1 ];
        a[ index1 ] = a[ index2 ];
        a[ index2 ] = tmp;
    }

    /**
     * Internal quicksort method that makes recursive calls.
     * Uses median-of-three partitioning and a cutoff of 10.
     * @param a an array of Comparable items.
     * @param low the left-most index of the subarray.
     * @param high the right-most index of the subarray.
     */
    private static void quicksort( Comparable [ ] a, int low, int high )
    {
        if( low + CUTOFF > high )
            insertionSort( a, low, high );
        else
        {
                // Sort low, middle, high
            int middle = ( low + high ) / 2;
            if( a[ middle ].compareTo( a[ low ] ) < 0 )
                swapReferences( a, low, middle );
            if( a[ high ].compareTo( a[ low ] ) < 0 )
                swapReferences( a, low, high );
            if( a[ high ].compareTo( a[ middle ] ) < 0 )
                swapReferences( a, middle, high );

                // Place pivot at position high - 1
            swapReferences( a, middle, high - 1 );
            Comparable pivot = a[ high - 1 ];

                // Begin partitioning
            int i;
            int j;
            for( i = low, j = high - 1; ; )
            {
		do {
		    i++;
		} while( a[ i ].compareTo( pivot ) < 0 );

		do {
		    j--;
		} while( pivot.compareTo( a[ j ] ) < 0 );

                if( i >= j )
                    break;
                swapReferences( a, i, j );
            }

                // Restore pivot
            swapReferences( a, i, high - 1 );

            quicksort( a, low, i - 1 );    // Sort small elements
            quicksort( a, i + 1, high );   // Sort large elements
        }
    }

    /**
     * Internal insertion sort routine for subarrays
     * that is used by quicksort.
     * @param a an array of Comparable items.
     * @param low the left-most index of the subarray.
     * @param n the number of items to sort.
     */
    private static void insertionSort( Comparable [ ] a, int low, int high )
    {
        for( int p = low + 1; p <= high; p++ )
        {
            Comparable tmp = a[ p ];
            int j;

            for( j = p; j > low && tmp.compareTo( a[ j - 1 ] ) < 0; j-- )
                a[ j ] = a[ j - 1 ];
            a[ j ] = tmp;
        }
    }

    @Override
    public void sort(Comparable[] a) {
        sort( a, 0, a.length - 1 );
        
    }
    
    
    private void sort( Comparable [ ] a, int low, int high )
    {
        if( low + CUTOFF > high )
            insertionSortT( a, low, high );
        else
        {
                // Sort low, middle, high
            int middle = ( low + high ) / 2;
            ++compares;
            if( a[ middle ].compareTo( a[ low ] ) < 0 )
                swapReferencesT( a, low, middle );

            ++compares;
            if( a[ high ].compareTo( a[ low ] ) < 0 )
                swapReferencesT( a, low, high );

            ++compares;
            if( a[ high ].compareTo( a[ middle ] ) < 0 )
                swapReferencesT( a, middle, high );

                // Place pivot at position high - 1
            swapReferencesT( a, middle, high - 1 );
            Comparable pivot = a[ high - 1 ];

                // Begin partitioning
            int i;
            int j;
            for( i = low, j = high - 1; ; )
            {
        do {
            i++;
            ++compares;
        } while( a[ i ].compareTo( pivot ) < 0 );

        do {
            j--;
            ++compares;
        } while( pivot.compareTo( a[ j ] ) < 0 );

                if( i >= j )
                    break;
                swapReferencesT( a, i, j );
            }

                // Restore pivot
            swapReferencesT( a, i, high - 1 );

            sort( a, low, i - 1 );    // Sort small elements
            sort( a, i + 1, high );   // Sort large elements
        }
    }
    
    public final void swapReferencesT( Object [ ] a, int index1, int index2 )
    {
        Object tmp = a[ index1 ];
        a[ index1 ] = a[ index2 ];
        a[ index2 ] = tmp;
    }
    
    public final void insertionSortT(Comparable [ ] a, int low, int high){
        for( int p = low + 1; p <= high; p++ )
        {
            Comparable tmp = a[ p ];
            int j;

            boolean enteredLoop = false;
            int compare=-1;
            
            for( j = p; j > low && (compare = tmp.compareTo( a[ j - 1 ] )) < 0; j-- ){
                a[ j ] = a[ j - 1 ];
                ++compares;
                enteredLoop = true;
            }
            if (!enteredLoop || compare >= 0){
                ++compares;
            }
            a[ j ] = tmp;
        }
    }


}